/*
 * logica.c
 *
 *  Created on: 22-mar-2010
 *      Author: pablo
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sys/socket.h>
#include "logica.h"
#include "message.h"
#include "constantes.h"
#include "configuracion.h"
#include "listas.h"
#include "consulta.h"
#include "variables.h"

int tratarQuery(struct message * msg);
int tratarQueryHit(struct message * msg);
int tratarBye(struct message * msg);

pthread_t hilo_conexiones; 	//controla las conexiones
pthread_t hilo_rowebcache; 	//controla rowebcache
pthread_t hilo_servent;		//controla servidor, hilo padre, original
pthread_t hilo_mensajes;	//controla la mensajeria
pthread_t hilo_update;		//controla la actualizacion
pthread_mutex_t mutexSocket = PTHREAD_MUTEX_INITIALIZER;




int aceptarConexion(){
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t --Inicio en 'aceptarConexion() de logica.c --\n");
	//return 1;
#endif
	int escucha;
	int soc;
	int sig=B_FALSE;
	struct sockaddr_in dirServ;
	struct sockaddr_in dirCliente;
	struct serventNode *nuevo;
	socklen_t tam;
	bzero(&dirServ, sizeof (struct sockaddr_in));
	dirServ.sin_family = AF_INET;
	dirServ.sin_addr.s_addr = htonl (INADDR_ANY);
	dirServ.sin_port = htons (puertoLocal);
	escucha = socket (AF_INET, SOCK_STREAM, 0);
	bind(escucha, (struct sockaddr *) &dirServ, sizeof (struct sockaddr ));
	listen(escucha, BIND_COLA);
	while(B_TRUE){
		sig=B_FALSE;
		tam = sizeof (struct sockaddr);
		if ((soc = accept (escucha, (struct sockaddr *)&dirCliente, &tam)) < 0 ){
			if ( errno != EINTR ) printf("Error en el hilo de conexion \n");
			else sig=B_TRUE; //si no hubo conexion paso al siguiente intento
		}
		if(sig==B_FALSE){
			pthread_mutex_lock( &mutexSocket );
			nuevo = malloc (sizeof (struct serventNode));
			nuevo->socket_fd = soc;
			memcpy (&(nuevo->addr), &dirCliente, sizeof (struct sockaddr));
			pthread_mutex_unlock(&mutexSocket);
			if ( (recibirRespuestaConexion(soc) >= B_TRUE) && (enviarRespuestaServidor(soc) >= B_TRUE)
					&& (enviarRespuestaCliente(soc)>=B_TRUE))
				insertNodeConectado(nuevo);
			else
				if(nuevo) free (nuevo);
		}
	}
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -1-fin en 'aceptarConexion() de logica.c, salida=%i --\n", B_TRUE);
	//return 1;
#endif
	return B_TRUE;
}

int conectarRowebCache(){
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t --Inicio en 'conectarRowebCache() de logica.c --\n");
	//return 1;
#endif
	struct sockaddr_in dirServ;
	char hostname[MAXHOSTNAMELEN];
	struct hostent *h;
	char *temp;
	char *aux;
	int soc;

	temp =strdup(URLrowebcache);
	aux = strstr(temp,HTTP_DB);
	free(temp);
	aux=&aux[HTTP_DB_LONG];
	sprintf(hostname,"%s",strtok(aux,SEPARADOR));
	soc = socket (AF_INET, SOCK_STREAM, 0);
	bzero (&dirServ,sizeof(struct sockaddr));
	dirServ.sin_port=htons(PUERTO_DEFECTO_RWC);
	dirServ.sin_family = AF_INET;
	h = gethostbyname(hostname);
	memcpy(&dirServ.sin_addr,h->h_addr,sizeof(struct sockaddr_in));

	if(connect(soc,(struct sockaddr *)&dirServ,sizeof(struct sockaddr))>=0){
		if((envioHostfileRequest(soc)==B_TRUE) && (recibirRespuestaHostfileRequest(soc)==B_TRUE))
			conectarAServidor();
	}else{
		printf("Error tratando de conectar con ROWEBCACHE \n");
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -1-fin en 'conectarRowebCache() de logica.c, salida=%i --\n", B_FALSE);
	//return 1;
#endif
		return B_FALSE;
	}
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -2-fin en 'conectarRowebCache() de logica.c, salida=%i --\n", B_TRUE);
	//return 1;
#endif
	return B_TRUE;
}

int conectarAServidor(){
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t --Inicio en 'conectarAServidor() de logica.c --\n");
	//return 1;
#endif
	unsigned int soc;
	int sal = B_FALSE;
	struct serventNode *n;
	n = getNodoCache();
	while (n!=NULL /*lista_servents.numero_servents_conectado*/ ){
		soc = socket (AF_INET, SOCK_STREAM, 0);
		setsockopt(soc, SOL_SOCKET, SO_KEEPALIVE, NULL, sizeof (NULL));
		setsockopt(soc, SOL_SOCKET, SO_REUSEADDR, NULL, sizeof (NULL));
		if((connect ( soc, (struct sockaddr *)&(n->addr), sizeof( struct sockaddr)) >= 0) &&
				(pedirConexion(soc)>=0) && (recibirRespuestaServidor(soc)==B_TRUE) &&
				(enviarRespuestaCliente(soc)>=0)){
			n->socket_fd = soc ;
			if(insertNodeConectado( n ) >= B_TRUE) sal=B_TRUE;
			else printf("ERROR al conectar a Servidor \n");
		}
		n = getNodoCache();
	}
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -1-fin en 'conectarAServidor() de logica.c, salida=%i --\n", sal);
	//return 1;
#endif
	return sal;
}

int nuevoFd_Set(fd_set *s){
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t --Inicio en 'nuevoFd_Set() de logica.c --\n");
	//return 1;
#endif
	int sal = 0;
	FD_ZERO(s);
	struct serventNode *n;
	nodo * aux;

	pthread_mutex_lock( &mutexListaServidores );
	resetLista( &listaServidores );
	aux = nextLista( &listaServidores );
	while (aux != NULL){
		n = (struct serventNode *)aux->datos;
		FD_SET(n->socket_fd, s);
		if ( n->socket_fd > sal )
			sal = n->socket_fd;
		aux = nextLista( &listaServidores );
	}
	pthread_mutex_unlock( &mutexListaServidores );
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -1-fin en 'nuevoFd_Set() de logica.c, salida=%i --\n", sal);
	//return 1;
#endif
	return sal;
}


int recibirDeServidor( int soc ) {
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t --Inicio en 'recibirDeServidor() de logica.c --\n");
	//return 1;
#endif
	struct message * msg;
	msg = recibirMensaje( soc );
	if(msg == NULL){
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -1-fin en 'recibirDeServidor() de logica.c, salida=%i --\n", B_FALSE);
	//return 1;
#endif
		return B_FALSE;
	}
	if( msg->header->payload_type == PAYLOAD_QUERY ){
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -2-fin en 'recibirDeServidor() de logica.c, salida=%i --\n", B_FALSE);
	//return 1;
#endif
		return tratarQuery( msg );
	}else if( msg->header->payload_type == PAYLOAD_QUERY_HIT){
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -3-fin en 'recibirDeServidor() de logica.c, salida=%i --\n", B_FALSE);
	//return 1;
#endif
		return tratarQueryHit( msg );
	}else if( msg->header->payload_type == PAYLOAD_BYE ){
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -4-fin en 'recibirDeServidor() de logica.c, salida=%i --\n", B_FALSE);
	//return 1;
#endif
		return tratarBye( msg );
	}

	return B_FALSE;
}

int controlMensajes(){
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t --Inicio en 'controlMensajes() de logica.c --\n");
	//return 1;
#endif
//basicamente se lee un mensaje, se trata y se envia
	struct message * msg;
	struct timeval t;
	int max_l;
	fd_set fd_l;
	int max_e;
	fd_set fd_e;
	struct serventNode *n;
	while(B_TRUE){
		t.tv_sec = SEG_ESPERA;
		t.tv_usec = MICROS_ESPERA;
		usleep(t.tv_usec);
		max_l = nuevoFd_Set( &fd_l );
		max_e = nuevoFd_Set( &fd_e );
		if ( max_e >= max_l ) max_e = select ( max_e+1 , &fd_l, &fd_e, NULL, &t );
		else max_l = select ( max_l+1 , &fd_l, &fd_e, NULL, &t );
		n = getNodoConectado();
		if (n != NULL && FD_ISSET(n->socket_fd, &fd_l)==1)	recibirDeServidor(n->socket_fd);
		if (n != NULL && FD_ISSET(n->socket_fd, &fd_e)==1){ //tengo Nodo  y estoy listo para enviar
			msg = extractMessage();
			if( msg != NULL ) enviarMensaje(n->socket_fd, msg);
		}
		if (n != NULL) insertNodeConectado(n);
	}
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -1-fin en 'controlMensajes() de logica.c, salida=%i --\n", B_TRUE);
	//return 1;
#endif
	return B_TRUE;
}

int tratarQuery( struct message * msg ) {
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t --Inicio en 'tratarQuery() de logica.c --\n");
	//return 1;
#endif
	struct queryId * orig;
	struct serventNode * n;

	if( mensajeExpirado( msg ) == B_TRUE ) {
//TODO necesito de una funcion q compare la query con los archivos q comparto, ofrecido por Fran y uno de los dos lo ajusta
		if( /* buscar fichero pedido en el msg  == B_TRUE */ B_TRUE ) {
			// Si tenemos ficheros que concuerden con la
			// busqueda se informa al nodo del que proviene
			// la peticion
			enviarMensaje( msg->sockfd, msg );
		}
		if ( msg->header->ttl > 0 ){ /* si el mensaje no expiro se envia a todos
									los servents, pero no al origen q solo se marca */
			n = firstNodoConectado();
			while( n != NULL ) {
				if( n->socket_fd != msg->sockfd ) insertMessage( msg );
				n = nextNodoConectado(n);
			}
			orig = malloc( sizeof( struct queryId ) );
			strncpy( orig->id, msg->header->GUID, LENGTH_MESSAGE_ID );
			orig->sock = msg->sockfd;
			insertarQueryId(orig);
		}
	}
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -1-fin en 'tratarQuery() de logica.c, salida=%i --\n", B_TRUE);
	//return 1;
#endif
	return B_TRUE;

}

int tratarQueryHit(struct message * msg){
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t --Inicio en 'tratarQueryHit() de logica.c --\n");
	//return 1;
#endif
	struct queryId *q;
	struct fileQuery *fq;
	struct queryHitMessage *exito;
	struct resultSet *res;
	struct sockaddr_in soc;
	int i = 0;
	q = buscarQueryId( msg->header->GUID );
	if( q == NULL ) { //si hay exito buscando query se reenvia
		msg->sockfd = q->sock;
		insertMessage( msg );
	} else { //si no lo hay nos lo quedamos
		exito = (struct queryHitMessage *)msg->header->payload;
		bzero(&soc, sizeof (struct sockaddr_in));
		soc.sin_family = AF_INET;
		soc.sin_addr.s_addr = exito->ip;
		soc.sin_port = exito->port;
		res = exito->result;
		while (i < exito->numExitos && res != NULL){
			fq = malloc (sizeof (struct fileQuery));
			encontrarQuery (fq, res->nombreArchivo, res->indiceArchivo, res->tamArchivo, (struct sockaddr *)&soc);
			insertEncontrado(fq);
			res = res->next;
			++i;
		}
		/* ¿¿interesa q salga por pantalla??? */
	}
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -1-fin en 'tratarQueryHit() de logica.c, salida=%i --\n", B_TRUE);
	//return 1;
#endif
	return B_TRUE;
}

int tratarBye( struct message * msg ) {
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t --Inicio en 'tratarBye() de logica.c --\n");
	//return 1;
#endif
	//TODO
	/* ¿q se hace?? */
	printf("he llegado a tratar un BYE que acabo de recibir, q hago?? \n");

#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -1-fin en 'tratarBye() de logica.c, salida=%i --\n", B_FALSE);
	//return 1;
#endif
	return B_FALSE;
}

int ActualizarRowebcache(){
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t --Inicio en 'ActualizarRowebcache() de logica.c --\n");
	//return 1;
#endif
	int res = B_TRUE;
	int sock;
	struct sockaddr_in soc;
	char hostname[MAXHOSTNAMELEN];
	struct hostent *host;
	char *rowebcache_temp;
	char *ptr;
	while ( res == B_TRUE ){
		rowebcache_temp =strdup(URLrowebcache);
		ptr = strstr(rowebcache_temp ,HTTP_DB);
		ptr=&ptr[6];
		sprintf(hostname,"%s",strtok ( ptr, SEPARADOR ));
		sock = socket (AF_INET, SOCK_STREAM, 0);
		bzero (&soc, sizeof (struct sockaddr));
		soc.sin_family = AF_INET;
		soc.sin_port = htons (PUERTO_DEFECTO_RWC);
		host = gethostbyname(hostname);
		if ( host != NULL ){
			memcpy(&soc.sin_addr,host->h_addr, sizeof (struct sockaddr_in));
			if(connect(sock,(struct sockaddr *)&soc, sizeof (struct sockaddr))<0){
				perror("Error en updateRequest \n");
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -1-fin en 'ActualizarRowebcache() de logica.c, salida=%i --\n", B_FALSE);
	//return 1;
#endif
				return B_FALSE;
			}else{
				if (envioUpdateRequest(sock)>=0){
					if (recibirRespuestaUpdate(sock)==B_FALSE)
						res = B_FALSE;
				}else{
					res = B_FALSE;
				}
				sleep (SEG_ACTUALIZACION);
			}
			free(rowebcache_temp);
		}
	}
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -2-fin en 'ActualizarRowebcache() de logica.c, salida=%i --\n", res);
	//return 1;
#endif
	return res;
}

int nuevoServent(){
	#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t --Inicio en 'nuevoServent() de logica.c --\n");
	//return 1;
	#endif
	int sal=B_TRUE;
	sal=pthread_mutex_init(&mutexSocket,NULL);
	if(sal<=B_FALSE){
		#ifdef DEBUG_MENSAJE
			fprintf(stderr,"\t -1-fin en 'nuevoServent() de logica.c, salida=%i --\n", B_FALSE);
		//return 1;
		#endif
		return B_FALSE;
	}
	sal=pthread_create(&hilo_conexiones, NULL, (void *)&aceptarConexion, NULL);
	if(sal<=B_FALSE){
		#ifdef DEBUG_MENSAJE
			fprintf(stderr,"\t -2-fin en 'nuevoServent() de logica.c, salida=%i --\n", B_FALSE);
			//return 1;
		#endif
		return B_FALSE;
	}
	sal=pthread_create(&hilo_update, NULL, (void *)&ActualizarRowebcache, NULL);
	if(sal<=B_FALSE){
		#ifdef DEBUG_MENSAJE
		fprintf(stderr,"\t -3-fin en 'nuevoServent() de logica.c, salida=%i --\n", B_FALSE);
		//return 1;
		#endif
		return B_FALSE;
	}
	sal=pthread_create(&hilo_rowebcache, NULL, (void *)&conectarRowebCache, NULL);
	if(sal<=B_FALSE){
		#ifdef DEBUG_MENSAJE
		fprintf(stderr,"\t -4-fin en 'nuevoServent() de logica.c, salida=%i --\n", B_FALSE);
		//return 1;
		#endif
		return B_FALSE;
	}
	sal=pthread_create(&hilo_mensajes, NULL, (void *)&controlMensajes, NULL);
	if(sal<=B_FALSE){
		#ifdef DEBUG_MENSAJE
		fprintf(stderr,"\t -5-fin en 'nuevoServent() de logica.c, salida=%i --\n", B_FALSE);
		//return 1;
		#endif
		return B_FALSE;
	}
	sal=pthread_detach(hilo_conexiones);
	if(sal<=B_FALSE){
		#ifdef DEBUG_MENSAJE
		fprintf(stderr,"\t -6-fin en 'nuevoServent() de logica.c, salida=%i --\n", B_FALSE);
		//return 1;
		#endif
		return B_FALSE;
	}
	sal=pthread_detach(hilo_rowebcache);
	if(sal<=B_FALSE){
		#ifdef DEBUG_MENSAJE
		fprintf(stderr,"\t -7-fin en 'nuevoServent() de logica.c, salida=%i --\n", B_FALSE);
		//return 1;
		#endif
		return B_FALSE;
	}
	sal=pthread_detach(hilo_mensajes);
	if(sal<=B_FALSE){
		#ifdef DEBUG_MENSAJE
			fprintf(stderr,"\t -8-fin en 'nuevoServent() de logica.c, salida=%i --\n", B_FALSE);
			//return 1;
		#endif
		return B_FALSE;
	}
	sal=pthread_detach(hilo_update);
	if(sal<=B_FALSE){
		#ifdef DEBUG_MENSAJE
			fprintf(stderr,"\t -9-fin en 'nuevoServent() de logica.c, salida=%i --\n", B_FALSE);
		//return 1;
		#endif
		return B_FALSE;
	}
	#ifdef DEBUG_MENSAJE
		fprintf(stderr,"\t -10-fin en 'nuevoServent() de logica.c, salida=%i --\n", B_TRUE);
		//return 1;
	#endif
	return B_TRUE;
}

int inicializarServidor(){
	#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t --Inicio en 'inicializarServidor() de logica.c --\n");
	//return 1;
	#endif
	inicializarListas();
	#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -1-fin en 'inicializarServidor() de logica.c, salida=lo q devuelva nuevoServent() --\n");
	//return 1;
	#endif
	return nuevoServent();
}

int finalizarServidor(){
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t --Inicio en 'finalizarServidor() de logica.c --\n");
	//return 1;
#endif
	int sal=B_TRUE;
	sal=pthread_mutex_destroy(&mutexSocket);
	if(sal<=B_FALSE){
#ifdef DEBUG_MENSAJE
		fprintf(stderr,"\t -1-fin en 'finalizarServidor() de logica.c, salida=%i --\n", B_FALSE);
		//return 1;
#endif
		return B_FALSE;
	}
	sal=freeListas();
	if(sal<=B_FALSE){
#ifdef DEBUG_MENSAJE
		fprintf(stderr,"\t -2-fin en 'finalizarServidor() de logica.c, salida=%i --\n", B_FALSE);
		//return 1;
#endif
		return B_FALSE;
	}
	sal=pthread_join(hilo_conexiones,NULL);
	if(sal<=B_FALSE){
#ifdef DEBUG_MENSAJE
		fprintf(stderr,"\t -3-fin en 'finalizarServidor() de logica.c, salida=%i --\n", B_FALSE);
		//return 1;
#endif
		return B_FALSE;
	}
	sal=pthread_join(hilo_rowebcache,NULL);
	if(sal<=B_FALSE){
#ifdef DEBUG_MENSAJE
		fprintf(stderr,"\t -4-fin en 'finalizarServidor() de logica.c, salida=%i --\n", B_FALSE);
		//return 1;
#endif
		return B_FALSE;
	}
	sal=pthread_join(hilo_mensajes,NULL);
	if(sal<=B_FALSE){
#ifdef DEBUG_MENSAJE
		fprintf(stderr,"\t -5-fin en 'finalizarServidor() de logica.c, salida=%i --\n", B_FALSE);
		//return 1;
#endif
		return B_FALSE;
	}
	sal=pthread_join(hilo_update,NULL);
	if(sal<=B_FALSE){
#ifdef DEBUG_MENSAJE
		fprintf(stderr,"\t -6-fin en 'finalizarServidor() de logica.c, salida=%i --\n", B_FALSE);
		//return 1;
#endif
		return B_FALSE;
	}
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -7-fin en 'finalizarServidor() de logica.c, salida=%i --\n", B_TRUE);
	//return 1;
#endif
	return B_TRUE;
}

int mostrarNodosServidor(){
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t --Inicio en 'mostrarNodosServidor() de logica.c --\n");
	//return 1;
#endif
	int sal=B_TRUE;
	sal=mostrarNodosCache();
	if(sal<=B_FALSE) return B_FALSE;
	printf("Conexiones Abiertas....\n");
	sal=mostrarNodosConectados();
	if(sal<=B_FALSE){
#ifdef DEBUG_MENSAJE
		fprintf(stderr,"\t -1-fin en 'mostrarNodosServidor() de logica.c, salida=%i --\n", B_FALSE);
		//return 1;
#endif
		return B_FALSE;
	}
#ifdef DEBUG_MENSAJE
	fprintf(stderr,"\t -2-fin en 'mostrarNodosServidor() de logica.c, salida=%i --\n", B_TRUE);
	//return 1;
#endif
	return B_TRUE;
}

